/**
 * @description		判断字符串数组或者对象是否为空
 * @author			hauner
 * @date			2022-06-14
 */
import { MessageBox } from 'element-ui'
import { exportExcelMethod } from './tool/excle'
import format from './tool/formats'
export const formats = format
formats.rmbig = BigMoney
export function isEmpty(e) {
  if (e === undefined || e === null) return true
  if (Array.isArray(e)) return !e.length
  if (e.constructor == Map || e.constructor == Set) return !e.size
  return !Boolean(Object.keys(e).length)
}

export function deepClone(obj, cache = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj
  if (cache.has(obj)) return cache.get(obj)
  let clone
  if (obj instanceof Date) {
    clone = new Date(obj.getTime())
  } else if (obj instanceof RegExp) {
    clone = new RegExp(obj)
  } else if (obj instanceof Map) {
    clone = new Map(Array.from(obj, ([key, value]) => [key, deepClone(value, cache)]))
  } else if (obj instanceof Set) {
    clone = new Set(Array.from(obj, value => deepClone(value, cache)))
  } else if (Array.isArray(obj)) {
    clone = obj.map(value => deepClone(value, cache))
  } else if (Object.prototype.toString.call(obj) === '[object Object]') {
    clone = Object.create(Object.getPrototypeOf(obj))
    cache.set(obj, clone)
    for (const [key, value] of Object.entries(obj)) {
      clone[key] = deepClone(value, cache)
    }
  } else {
    clone = Object.assign({}, obj)
  }
  cache.set(obj, clone)
  return clone
}

export function formatTime(fmt = 'yyyy-MM-dd', date = new Date()) {
  let ret
  date = new Date(date)
  if (!(date instanceof Date)) throw Error(`${date.toString}不是一个正确的时间`)
  const opt = {
    'y+': date.getFullYear().toString(), // 年
    'M+': (date.getMonth() + 1).toString(), // 月
    'd+': date.getDate().toString(), // 日
    'h+': date.getHours().toString(), // 时
    'm+': date.getMinutes().toString(), // 分
    's+': date.getSeconds().toString() // 秒
    // 有其他格式化字符需求可以继续添加，必须转化成字符串
  }
  for (let k in opt) {
    ret = new RegExp('(' + k + ')').exec(fmt)
    if (ret) {
      fmt = fmt.replace(ret[1], ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, '0'))
    }
  }
  return fmt
}

export function confirmSync(message = '确定要删除吗？', title='', type='warning') {
  return new Promise((succ, err) => {
    MessageBox.confirm( message, {
      title, 
      type,
      customClass: 'top--msg',
      dangerouslyUseHTMLString: true,
      callback: (action) => succ( action == 'confirm' )
    })
  })
}

/**
 * @description			转换为json字符串
 * @author				hauner
 * @param json   json字符串
 * @param {*} def 当json字符串不合法的时候返回的默认值
 * @data				2022-04-14
 */
export function toJsonObj(json, def) {
  if(!json) return def
  try {
    return JSON.parse(json)    
  } catch (e) {
    return def
  }
}

export function isJson(json) {
  try {
    JSON.parse(json)
    return true
  } catch (e) {
    return false
  }
}

export const toJson = toJsonObj

/**
 * @description			转换为json字符串
 * @author				hauner
 * @param json   json字符串
 * @param {*} def 当json字符串不合法的时候返回的默认值
 * @data				2022-04-14
 */
export function toJsonStr(json, def) {
  if(!json) return def
  try {
    return JSON.stringify(json)    
  } catch (e) {
    return def
  }
}

/************************************************************ 
 * @description			延时函数
 * @author			hauner
 * @date			2024-11-18
 ************************************************************/
export function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))  
}

/************************************************************ 
 * @description			金额转换为大写
 * @param str   金额字符串
 * @author			hauner
 * @date			2024-11-25
 ************************************************************/
export function BigMoney(str) {
  // 将传入的金额字符串赋值给变量money
  let money = str;
  // 如果money为空或未定义，则返回'零元整'
  if (!money) return '零元整';
  // 将money转换为字符串并去除其中的逗号
  money = money.toString().replace(/,/g, '');
  // 如果money不是一个有效的数字，则返回'零元整'
  if (isNaN(money)) return '零元整';
  // 如果money的整数部分超过14位，则抛出一个错误，提示金额过大
  if (parseInt(money) > 99999999999999) throw new Error('金额过大');
  // 将money按照小数点分割成整数部分和小数部分
  let list = money.split('.');
  // 存储整数部分
  let zheng = list[0];
  // 存储小数部分（最多两位），如果没有小数部分则默认为0
  let xiao = list[1] ? list[1].substr(0, 2) : 0;
  // 初始化结果字符串
  let result = '';
  // 数字0-9的中文大写映射
  let cnNum = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
  // 个、十、百、千的中文大写映射
  let unit = ['', '拾', '佰', '仟'];
  // 万、亿、兆的中文大写映射
  let bUnit = ['', '万', '亿', '兆'];
  // 用于记录当前处理的万、亿、兆的级别
  let b = 0;
  // 如果整数部分为0，则直接将结果设置为'零'
  if (zheng == 0) {
    result = '零';
  } else {
    // 从右向左每四位一组处理整数部分，依次添加万、亿、兆等单位
    for (let i = 0; zheng > 0; i++) {
      if (i % 4 == 0) {
        result = bUnit[b++] + result;
        i = 0;
      }
      // 使用cnNum和unit将每一位数字转换为中文大写
      result = cnNum[zheng % 10] + unit[i] + result;
      // 去掉已经处理过的最后一位数字
      zheng = Math.floor(zheng / 10);
    }
  }
  // 去除连续的零
  result = result
    .replace(/零(佰|仟|拾)/g, '零')
    .replace(/零+/g, '零')
    .replace(/(亿|兆|万|仟|佰|拾)零$/g, '$1')
    .replace(/零(亿|万|兆)/g, '$1')
    .replace(/亿万/g, '亿')
    .replace(/兆亿/g, '兆');
  // 初始化小数部分的中文大写字符串
  let xrs = '';
  // 如果小数部分不为0，则将其转换为角和分的中文大写
  if (xiao > 0) {
    let list = xiao.split('');
    let jiao = list[0];
    let fen = list[1];
    // 根据角和分的值生成中文大写字符串
    if(jiao > 0 && fen > 0){
      xrs = `${cnNum[jiao]}角${cnNum[fen]}分`;
    } else if(jiao > 0 && !(+fen) ){
      xrs = `${cnNum[jiao]}角整`;
    } else if(!(+jiao) && fen > 0){
      xrs = `零${cnNum[fen]}分`; 
    }
    // 在结果字符串中添加'元'
    result += '元';
  } else {
    // 如果小数部分为0，则添加'元整'
    result += '元整';
  }
  // 返回转换后的中文大写金额
  return result + xrs;
}
/************************************************************ 
 * @description		数字格式化整数不加小数点
 * @param num   数字
 * @param len   小数位数
 * @author			hauner
 * @date			2024-11-27
 ************************************************************/
export function numFixed(num, len = 2) {
  let res = Number(num)
  let fix = res.toFixed(len)
  return fix == res ? res : fix
}
export const exportExcelFunc = exportExcelMethod

export function exportMethodByUrl({ url, name = '未命名文件', fileName }) {
  const link = document.createElement('a')
  link.style.display = 'none'
  link.href = url
  link.download = fileName || name //下载的文件名
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
}

// 下载指定文件
export function fetchDownloadUrl(file) {
  let {url, fileName = '未命名文件'} = file
  fetch(url).then(res => res.blob()).then(blob => {
    let a = document.createElement('a')
    a.style.display = 'none'
    document.body.appendChild(a)
    let u = window.URL.createObjectURL(blob)
    a.href = u
    a.download = fileName
    a.click()
    window.URL.revokeObjectURL(u)
    document.body.removeChild(a)
  })
}

export function exportExcelByBlob(blob, fileName = '未命名文件.xlsx') {
  let a = document.createElement('a')
    a.style.display = 'none'
    document.body.appendChild(a)
    let u = window.URL.createObjectURL(blob)
    a.href = u
    a.download = fileName
    a.click()
    window.URL.revokeObjectURL(u)
    document.body.removeChild(a)
}

export async function onLoading(loading, fn) {
  try{
    loading = true
    await fn()
    loading = false
  }catch(e){
    loading = false
    console.error(e, "error")
  }
}


export function isArray(arg) {
  if (typeof Array.isArray === 'undefined') {
    return Object.prototype.toString.call(arg) === '[object Array]'
  }
  return Array.isArray(arg)
}

export function max(max) {
  return {
    validator: (r, v, cb) => {
      if (v === null || v === undefined) return cb()
      v = v.toString()
      if (v.length > max) cb(`最大长度为${max}`)
      cb()
    },
    trigger: ['change', 'blur']
  }
}

export function required(message, type) {
  return { required: true, message, trigger: ['change', 'blur'], type }
}

export const isRequired = required

export function pattern(pattern, msg) {
  return {
    validator: (r, v, c) => {
      if (!v || v === 0) {
        c()
        return
      }
      v = v.toString()
      v.match(pattern) ? c() : c(`格式错误${msg ? `：${msg}` : ''}`)
      c()
    },
    trigger: ['change', 'blur']
  }
}

export const isPattern = pattern

export function patternValue(v, pattern, msg) {
  if (!v || v === 0) {
    return
  }
  v = v.toString()
  if (!v.match(pattern)) Message.error(`格式错误${msg ? `：${msg}` : ''}`)
}

export function getEmitPath(list, tree, key = 'id', children = 'children') {
  let selfTree = JSON.parse(JSON.stringify(tree))
  let keyMap = tree2map(tree, {}, 0, key, children )
  let emitPath = []
  list.forEach(el => {
      let arr = getChainParent(el, keyMap)
      emitPath.push(arr)
  })
  return emitPath
}

export function tree2map(tree, map = {}, pk = 0, key = 'id', children = 'children') {
  tree.forEach( item => {
      item.pk = pk
      map[item[key]] = item
      if(item[children]) tree2map(item[children], map, item[key])
  })
  return map
}

export function getLeaf(tree, children = 'children', list = []) {
  tree.forEach( item => {
    if(item[children]) getLeaf(item[children], children, list)
    else list.push(item)
  })
  return list
}

export function getChainParent(k, map, pk = 'pk', list = []) {
  let item = map[k]
  list.unshift(k)
  if(item[pk] != 0) getChainParent(item[pk], map, pk, list)
  return list
}

export function splitBothSide(str, left = '\\(', right = '\\)') {
  let reg = new RegExp(`.*${left}(.*)${right}.*`)
  return str.replace(reg, '$1')
}


export default {
  exportMethodByUrl, fetchDownloadUrl, deepClone,
  isEmpty, formatTime, toJsonObj, confirmSync, toJsonStr, 
  toJson, BigMoney, sleep, numFixed, exportExcelMethod,
  onLoading, getChainParent, tree2map, getEmitPath, isArray, 
  max, required, pattern, patternValue, isJson, splitBothSide,
}